/*******************************************************************************
 * Copyright (C) 2015 Maxim Integrated Products, Inc., All Rights Reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL MAXIM INTEGRATED BE LIABLE FOR ANY CLAIM, DAMAGES
 * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 *
 * Except as contained in this notice, the name of Maxim Integrated
 * Products, Inc. shall not be used except as stated in the Maxim Integrated
 * Products, Inc. Branding Policy.
 *
 * The mere transfer of this software does not imply any licenses
 * of trade secrets, proprietary technology, copyrights, patents,
 * trademarks, maskwork rights, or any other form of intellectual
 * property whatsoever. Maxim Integrated Products, Inc. retains all
 * ownership rights.
 *
 * $Id: usb_app.c 16374 2015-01-16 21:27:43Z jeremy.brodt $
 *
 *******************************************************************************
 */

/** Global includes */
#include <config.h>
#include <errors.h>
/** Other includes */
#include <mml.h>
#include <mml_gcr.h>
#include <mml_gpio.h>
#include <mml_intc.h>
#include <mml_usb_regs.h>  // MXC_USB_MAX_PACKET
/** Local includes */
#include <private.h>
#include <printf_lite.h>

#include "usb.h"
#include "usb_event.h"
#include "enumerate.h"

#include "cdc_acm.h"
#include "descriptors.h"

#define MML_VBUS_GPIO_MASK 1
#define ACM_BUFFER_DEPTH   64

void USB_IRQHandler(void);

/***** File Scope Variables *****/

//static uint8_t acm_data_buffer[ACM_BUFFER_DEPTH];

/* This EP assignment must match the Configuration Descriptor */
static const acm_cfg_t acm_cfg = {
  1,                  /* EP OUT */
  MXC_USB_MAX_PACKET, /* OUT max packet size */
  2,                  /* EP IN */
  MXC_USB_MAX_PACKET, /* IN max packet size */
  3,                  /* EP Notify */
  MXC_USB_MAX_PACKET, /* Notify max packet size */
};

static volatile int usb_read_complete;

/***** Function Prototypes *****/
static int setconfig_callback(usb_setup_pkt *sud, void *cbdata);
static int event_callback(maxusb_event_t evt, void *data);
static void read_from_usb(void);
static int configure_uart(void);
static int usb_read_callback(void);



/******************************************************************************/
int usb_app_init(void )
{
	volatile mml_gpio_regs_t				*reg_gpio = (volatile mml_gpio_regs_t*)MML_GPIO2_IOBASE;

  int err;

	/** Init USB */
	
	/* gpio pin config (VBUS) Alternate function 1, High Z, GPIO2, bit 0*/
	reg_gpio->en &= ~MML_VBUS_GPIO_MASK;
	reg_gpio->en1 &= ~MML_VBUS_GPIO_MASK;
	reg_gpio->pad_cfg1 &= ~MML_VBUS_GPIO_MASK;
	reg_gpio->pad_cfg2 &= ~MML_VBUS_GPIO_MASK;
	
	/** USB init */
	if ((err = usb_init(NULL)) != NO_ERROR) {
		
		return err;
	}
  /* Initialize the enumeration module */
  if ((err = enum_init()) != 0) {
		
    return err;
  }

  /* Register enumeration data */
  enum_register_descriptor(ENUM_DESC_DEVICE, (uint8_t*)&device_descriptor, 0);
  enum_register_descriptor(ENUM_DESC_CONFIG, (uint8_t*)&config_descriptor, 0);
  enum_register_descriptor(ENUM_DESC_STRING, lang_id_desc, 0);
  enum_register_descriptor(ENUM_DESC_STRING, mfg_id_desc, 1);
  enum_register_descriptor(ENUM_DESC_STRING, prod_id_desc, 2);

  /* Handle configuration */
  enum_register_callback(ENUM_SETCONFIG, setconfig_callback, NULL);

  /* Initialize the class driver */
  if ((err = acm_init()) != 0) {
    return err;
  }

  /* Register callbacks */
  usb_event_enable(MAXUSB_EVENT_NOVBUS, event_callback, NULL);
  usb_event_enable(MAXUSB_EVENT_VBUS, event_callback, NULL);
  usb_event_enable(MAXUSB_EVENT_BRST, event_callback, NULL);
  acm_register_callback(ACM_CB_SET_LINE_CODING, configure_uart);
  acm_register_callback(ACM_CB_READ_READY, usb_read_callback);
  usb_read_complete = 0;

 	mml_intc_setup_irq(MML_INTNUM_USB, MML_INTC_PRIO_2,  USB_IRQHandler);
	mml_intc_enable_irq(MML_INTNUM_USB);

  return 0;
}

/******************************************************************************/
static void read_from_usb(void)
{
	u8 chars;

	if ((chars = acm_canread()) > 0) {
#if  1
		if (acm_read(&chars, 1) != chars){
			usb_rcv_buf_push(chars);
		}

#else
		if (chars > ACM_BUFFER_DEPTH) {
			chars = ACM_BUFFER_DEPTH;
		}

		if (acm_read(acm_data_buffer, chars) != chars) {
			usb_debug("acm_read() failed\n");
		} 
		else {
			acm_write(acm_data_buffer, chars);
		}
#endif		
	}
}

/******************************************************************************/
void usb_app_task(void *pvParameters)
{
  /* Process incoming USB data. */
	usb_echo_init();
  
	while(1)
	{
		read_from_usb();

		usb_data_parse_pkg();
	}
}



//******************************************************************************
static int configure_uart(void)
{
  /*
	  As this example simply echo characters, this function is not needed.
  	It is called when the serial port line settings are changed.
	  Baudrate, databits, parity changes, etc.
	*/
	
  return 0;
}

/******************************************************************************/
static int setconfig_callback(usb_setup_pkt *sud, void *cbdata)
{
  /* Confirm the configuration value */
  if (sud->wValue == config_descriptor.config_descriptor.bConfigurationValue) {
    usb_debug("Enumeration complete. Waiting for characters...\n");
    return acm_configure(&acm_cfg); /* Configure the device class */
  } else if (sud->wValue == 0) {
    return acm_deconfigure();
  }

  return -1;
}

/******************************************************************************/
static int event_callback(maxusb_event_t evt, void *data)
{
  switch (evt) {
    case MAXUSB_EVENT_NOVBUS:
      usb_disconnect();
      enum_clearconfig();
      acm_deconfigure();
      usb_debug("VBUS disconnected\n");
      break;
    case MAXUSB_EVENT_VBUS:
      usb_connect();
      usb_debug("VBUS detected\n");
      break;
    case MAXUSB_EVENT_BRST:
      enum_clearconfig();
      acm_deconfigure();
      break;
    default:
      break;
  }

  return 0;
}

/******************************************************************************/
static int usb_read_callback(void)
{
  usb_read_complete = 1;
  return 0;
}

//******************************************************************************
void USB_IRQHandler(void)
{
  usb_event_handler();
}
